﻿using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using web_api.Entities;
using web_api.Models;
using web_api.Services;

namespace web_api.Controllers
{
  [Route("api/companies/{companyId}/employees")]
  [ApiController]
  public class EmployeesController : ControllerBase
  {
    private readonly ICompanyRepository _companyRepository;
    private readonly IMapper _mapper;
    public EmployeesController(ICompanyRepository companyRepository, IMapper mapper)
    {
      _companyRepository = companyRepository ?? throw new ArgumentNullException(nameof(companyRepository));
      _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
    }
    [HttpGet]
    public async Task<ActionResult<IEnumerable<EmployeeDto>>> 
      GetEmployeesForComapany(Guid companyId, [FromQuery(Name ="gender")] string genderDisplay,string q)
    {
      if (!await _companyRepository.CompanyExistsAsync(companyId)) return NotFound();
      var employees = await _companyRepository.GetEmployeesAsync(companyId,genderDisplay,q);
      var employeesDtos = _mapper.Map<IEnumerable<EmployeeDto>>(employees);
      return Ok(employeesDtos);
    }
    [HttpGet("{employeeId}",Name =nameof(GetEmployeeForComapany))]
    public async Task<ActionResult<IEnumerable<EmployeeDto>>> GetEmployeeForComapany(Guid companyId, Guid employeeId)
    {
      if (!await _companyRepository.CompanyExistsAsync(companyId)) return NotFound();
      var employee = await _companyRepository.GetEmployeeAsync(companyId,employeeId);
      if (employee == null) return NotFound();
      var employeesDto = _mapper.Map<EmployeeDto>(employee);
      return Ok(employeesDto);
    }
    [HttpPost]
    public async Task<ActionResult<EmployeeDto>>
      GreateEmployeeForCompany(Guid companyId, EmployeeAddDto employee)
    {
      if (!await _companyRepository.CompanyExistsAsync(companyId))
      {
        return NotFound();
      }
      var entity = _mapper.Map<Employee>(employee);
      _companyRepository.AddEmployee(companyId, entity);
      await _companyRepository.SaveAsync();
      var dtoToReturn = _mapper.Map<EmployeeDto>(entity);
      return CreatedAtRoute(nameof(GetEmployeeForComapany), new { companyId , employeeId = dtoToReturn.Id }, dtoToReturn);
    }
    [HttpPut("{employeeId}")]
    public async Task<ActionResult<EmployeeDto>> UpdateEmployeeForCompany(Guid companyId,Guid employeeId,EmployeeUpdateDto employee) {
      if (!await _companyRepository.CompanyExistsAsync(companyId)) return NotFound();
      var employeeEntity = await _companyRepository.GetEmployeeAsync(companyId, employeeId);
      if (employeeEntity == null) {
        var employeeToAddEntity = _mapper.Map<Employee>(employee);
        employeeToAddEntity.Id = employeeId;
        _companyRepository.AddEmployee(companyId, employeeToAddEntity);
        await _companyRepository.SaveAsync();
        var dtoToReturn = _mapper.Map<EmployeeDto>(employeeToAddEntity);
        return CreatedAtRoute(nameof(GetEmployeeForComapany), new { companyId, employeeId = dtoToReturn.Id }, dtoToReturn);
      }
      _mapper.Map(employee, employeeEntity);
      _companyRepository.UpdateEmployee(employeeEntity);
      await _companyRepository.SaveAsync();
      return NoContent();
    }
    [HttpPatch("{employeeId}")]
    public async Task<IActionResult> PartiallyUpdateEmployeeForCompany
      (Guid companyId,Guid employeeId,JsonPatchDocument<EmployeeUpdateDto> patchDocument) {
      if (!await _companyRepository.CompanyExistsAsync(companyId)) return NotFound();
      var employeeEntity  = await _companyRepository.GetEmployeeAsync(companyId, employeeId);
      if (employeeEntity == null) {
        var employeeDto = new EmployeeUpdateDto();
        patchDocument.ApplyTo(employeeDto, ModelState);
        if (!TryValidateModel(employeeDto)) {
          return ValidationProblem(ModelState);
        }
        var employeeToAdd = _mapper.Map<Employee>(employeeDto);
        employeeToAdd.Id = employeeId;
        _companyRepository.AddEmployee(companyId, employeeToAdd);
        await _companyRepository.SaveAsync();
        var dtoToReturn = _mapper.Map<EmployeeDto>(employeeToAdd);
        return CreatedAtRoute(nameof(GetEmployeeForComapany), new { companyId, employeeId = dtoToReturn.Id }, dtoToReturn);
      }
      var dtoToPatch = _mapper.Map<EmployeeUpdateDto>(employeeEntity);
      //需要处理验证错误
      patchDocument.ApplyTo(dtoToPatch,ModelState);
      if (!TryValidateModel(dtoToPatch)) {
        return ValidationProblem(ModelState);
      }
      _mapper.Map(dtoToPatch, employeeEntity);
      _companyRepository.UpdateEmployee(employeeEntity);
      await _companyRepository.SaveAsync();
      return NoContent();
    }
    [HttpDelete("{employeeId}")]
    public async Task<IActionResult> DeleteEmployeeForCompany(Guid companyId,Guid employeeId) {
      if (!await _companyRepository.CompanyExistsAsync(companyId)) return NotFound();
      var employee = await _companyRepository.GetEmployeeAsync(companyId, employeeId);
      if (employee == null) return NotFound();
      _companyRepository.DeleteEmployee(employee);
      await _companyRepository.SaveAsync();
      return NoContent();
    }
    public override ActionResult ValidationProblem(ModelStateDictionary modelStateDictionary) {
      var options = HttpContext.RequestServices.GetRequiredService<IOptions<ApiBehaviorOptions>>();
      return (ActionResult)options.Value.InvalidModelStateResponseFactory(ControllerContext);
    }
  } 
}
